Repository: voxel51/fiftyone
Issue: [BUG] Cannot convert entire video dataset to frames view

On `fiftyone==0.14.0`, trying to convert an entire dataset to a frames view via the syntax:

```py
import fiftyone as fo
import fiftyone.zoo as foz
from fiftyone import ViewField as F

dataset = foz.load_zoo_dataset("quickstart-video")
frames = dataset.to_frames()
```

produces the following error:

```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-38108ee07947> in <module>
      4 
      5 dataset = foz.load_zoo_dataset("quickstart-video")
----> 6 frames = dataset.to_frames()

~/dev/fiftyone/fiftyone/core/collections.py in to_frames(self, **kwargs)
   4868             a :class:`fiftyone.core.video.FramesView`
   4869         """
-> 4870         return self._add_view_stage(fos.ToFrames(**kwargs))
   4871 
   4872     @classmethod

~/dev/fiftyone/fiftyone/core/dataset.py in _add_view_stage(self, stage)
   4048 
   4049     def _add_view_stage(self, stage):
-> 4050         return self.view().add_stage(stage)
   4051 
   4052     def _pipeline(

~/dev/fiftyone/fiftyone/core/collections.py in add_stage(self, stage)
   2244             a :class:`fiftyone.core.view.DatasetView`
   2245         """
-> 2246         return self._add_view_stage(stage)
   2247 
   2248     @view_stage

~/dev/fiftyone/fiftyone/core/view.py in _add_view_stage(self, stage)
    777 
    778         if stage.has_view:
--> 779             view = stage.load_view(self)
    780         else:
    781             view = copy(self)

~/dev/fiftyone/fiftyone/core/stages.py in load_view(self, sample_collection)
   5914             kwargs = self._config or {}
   5915             frames_dataset = fov.make_frames_dataset(
-> 5916                 sample_collection, **kwargs
   5917             )
   5918 

~/dev/fiftyone/fiftyone/core/video.py in make_frames_dataset(sample_collection, sample_frames, frames_patt, fps, max_fps, size, min_size, max_size, force_sample, sparse, verbose)
    487         max_fps,
    488         sparse,
--> 489         verbose,
    490     )
    491 

~/dev/fiftyone/fiftyone/core/video.py in _populate_frames(dataset, src_collection, frames_patt, force_sample, sample_frames, fps, max_fps, sparse, verbose)
    580         if sample_frame_numbers != []:
    581             id_map[video_path] = str(sample["_id"])
--> 582             sample_map[video_path].update(sample_frame_numbers)
    583 
    584         for frame_number in doc_frame_numbers:

TypeError: 'NoneType' object is not iterable
```


Changed files:
fiftyone/core/video.py

diff --git a/fiftyone/core/video.py b/fiftyone/core/video.py
index 8c2aeef3f..11a86d0b2 100644
--- a/fiftyone/core/video.py
+++ b/fiftyone/core/video.py
@@ -579,7 +579,10 @@ def _populate_frames(
         # note: [] means no frames, None means all frames
         if sample_frame_numbers != []:
             id_map[video_path] = str(sample["_id"])
-            sample_map[video_path].update(sample_frame_numbers)
+            if sample_frame_numbers is None:
+                sample_map[video_path] = None
+            elif sample_map[video_path] is not None:
+                sample_map[video_path].update(sample_frame_numbers)
 
         for frame_number in doc_frame_numbers:
             if is_clips:
@@ -617,7 +620,10 @@ def _populate_frames(
     frames_to_sample = []
     for video_path, sample_frame_numbers in sample_map.items():
         ids_to_sample.append(id_map[video_path])
-        frames_to_sample.append(sorted(sample_frame_numbers))
+        if sample_frame_numbers is not None:
+            sample_frame_numbers = sorted(sample_frame_numbers)
+
+        frames_to_sample.append(sample_frame_numbers)
 
     if not docs:
         return ids_to_sample, frames_to_sample